// read config file

#include <string.h>
#include "skip_file.h"

enum {
	T_NULL,
	T_NUMBER,
	T_STRING
};

enum {
	C_NULL,
	C_NUM,
	C_CHAR
};

static signed char char_mask[128] = {0};
#define char_type(c)				(char_mask[(int)(c)])
#define char_next(sf)				((*(sf->src)!='\0')?(*(sf->src)++):('\0'))
#define char_at(sf)					(*(sf->src))
#define char_watch(sf, c, r)		do{if(!char_at(sf) || *(sf->src)!=c) return (r); sf->src++;}while(0)			
#define space_watch(sf)				do{while(char_type(*(sf->src))==(byte)' ') char_next(sf);}while(0)

typedef struct  _field{
	char* key;
	byte type;
	union {
		int num;
		char* str;
	}value;
	struct _field* next;
} field;

typedef struct _sconf{
	FILE* fp;
	size_t c_size;
	field* field_list;
	char* src;
	int line;
	field* rp;
}sconf;

static void _skipC_initmask();
static int _skipC_loadconf(sconf* sf);
static void _skipC_clearfield(sconf* sf);
static int _skipC_parseconf(sconf* sf);
static int _skipC_readnum(sconf* sf);
static char* _skipC_readstr(sconf* sf);
static int _skipC_readfield(sconf* sf);
static char* _skipC_readide(sconf* sf);
static void _skipC_initmask();


void skipC_close(sconf* sf)
{
	if(sf)
	{
		fclose(sf->fp);
		_skipC_clearfield(sf);
		free(sf);
	}
}


// open conf file
sconf* skipC_open(const char* name)
{
	sconf* ret = NULL;
	long c_size=s_filesize(name);
	FILE* fp = fopen(name, "rb");
	
	if(fp==NULL)
		return NULL;
	if(c_size<=0)
	{
		fclose(fp);
		return NULL;
	}
	ret = (sconf*)malloc(sizeof(*ret)+c_size+1);
	ret->src = (char*)(ret+1);
	ret->fp = fp;
	ret->c_size = (size_t)c_size;
	ret->field_list = NULL;
	ret->src[c_size]=0;
	_skipC_initmask();
	if(_skipC_loadconf(ret)==SKIP_FALSE)
	{
		skipC_close(ret);
		return NULL;
	}
	
	return ret;
}

// next
char* skipC_next(sconf* sf, char* key)
{
	char* ret;
	if(!sf || !sf->rp)
		return NULL;
	if(key==NULL)
		sf->rp = sf->field_list;
	else
	{
		sf->rp = sf->rp->next;
		if(sf->rp==NULL)
			return NULL;
	}

	ret = sf->rp->key;
	return ret;
}

// read number
int skipC_readnum(sconf* sf)
{
	if(!sf || !sf->rp || sf->rp->type!=T_NUMBER)
		return 0;
	
	return sf->rp->value.num;
}

// read string
char* skipC_readstr(sconf* sf)
{
	if(!sf || !sf->rp || sf->rp->type!=T_STRING)
		return NULL;

	return sf->rp->value.str;
}

static void _skipC_clearfield(sconf* sf)
{
	while(sf->field_list)
	{
		void* p=sf->field_list;
		free(sf->field_list->key);
		if(sf->field_list->type == T_STRING)
			free(sf->field_list->value.str);
		sf->field_list = sf->field_list->next;
		free(p);
	}
}

// load config
static int _skipC_loadconf(sconf* sf)
{
	if(fread(sf->src, 1, sf->c_size, sf->fp) != sf->c_size)
		return SKIP_FALSE;
	
	return _skipC_parseconf(sf);
}

// parse config
static int _skipC_parseconf(sconf* sf)
{
	sf->line=1;
	while(char_at(sf))
	{
		switch(char_type(*(sf->src)))
		{
		case '#':		// pass
			do 
			{
				char_next(sf);
			} while (char_at(sf)!='\n' && char_at(sf)!=0);
			break;
		case  C_CHAR:	// key
			if(_skipC_readfield(sf)==SKIP_FALSE)
				return SKIP_FALSE;
			break;
		case  '\n':		// line
			(sf->line)++;
		case '\r':
		case '\t':
		case ' ':		//space
			char_next(sf);
			break;
		default:		// error
			return SKIP_FALSE;
		}
	}
	return SKIP_TRUE;
}

// read number
static int _skipC_readnum(sconf* sf)
{
	int ret;
	char b;
	char* s_num = sf->src;
	while(char_type(*(sf->src))==C_NUM)
		char_next(sf);
	b = char_at(sf);
	*(sf->src) = 0;
	ret = atoi(s_num);
	*(sf->src) = b;
	return ret;
}

// read string
static char* _skipC_readstr(sconf* sf)
{
	char* ret;
	char* p;
	char b;

	char_watch(sf, '"', NULL);
	p=sf->src;
	while(char_type(*(sf->src))!='"' && char_at(sf)!=0)
		char_next(sf);
	if(char_at(sf)==0)
		return NULL;

	b = *(sf->src);
	*(sf->src)=0;
	ret = (char*)malloc(sf->src-p+1);
	ret[sf->src-p]=0;
	memcpy(ret, p, sf->src-p);
	*(sf->src)=b;
	char_watch(sf, '"',(free(ret), NULL));
	return ret;
}

// read field
static int _skipC_readfield(sconf* sf)
{
	field* vp;
	field vv;
	vv.key = _skipC_readide(sf);
	space_watch(sf);					// pass space
	char_watch(sf, '=', SKIP_FALSE);	// watch '='
	space_watch(sf);					// pass space
	switch(char_type(*(sf->src)))
	{
	case C_NUM:		// number
		vv.type = T_NUMBER;
		vv.value.num = _skipC_readnum(sf);
		break;
	case '"':		// string
		vv.type = T_STRING;
		vv.value.str = _skipC_readstr(sf);
		if(vv.value.str==NULL)
			return SKIP_FALSE;
		break;
	default:
		return SKIP_FALSE;
	}
	vp = (field*)malloc(sizeof(*vp));
	*vp=vv;
	vp->next = sf->field_list;
	sf->rp = vp;
	sf->field_list = vp;
	return SKIP_TRUE;
}

// read ide
static char* _skipC_readide(sconf* sf)
{
	char* ret;
	byte t;
	char* p= sf->src;
	char b;
	while( (t=char_type(*(sf->src)))==C_CHAR || t==C_NUM )
	{
		char_next(sf);
	}
	b= *(sf->src);
	*(sf->src)=0;
	ret = malloc(sf->src-p+1);
	ret[sf->src-p]=0;
	memcpy(ret, p, sf->src-p);
	*(sf->src)=b;
	return ret;
}

static void _skipC_initmask()
{
	int i;
	for(i='1'; i<='9'; i++)
		char_mask[i] = C_NUM;
	for(i='a'; i<='z'; i++)
		char_mask[i] = C_CHAR;
	for(i='A';i<='Z';i++)
		char_mask[i] = C_CHAR;
	char_mask['_'] = C_CHAR;
	char_mask['#'] = '#';
	char_mask['='] = '=';
	char_mask['"'] = '"';
	char_mask['\n'] = '\n';
	char_mask[' '] = ' ';
	char_mask['\t'] = '\t';
	char_mask['\r'] = '\r';
}


